require "import"
import "android.widget.*"
import "android.view.*"
import "android.content.Intent"
import "android.net.Uri"
import "android.media.MediaPlayer"
import "java.util.Locale"
import "android.os.Handler"
import "java.lang.Runnable"
import "android.text.TextWatcher"
import "android.content.DialogInterface"
import "android.widget.AdapterView"
import "java.net.URLEncoder"
import "android.text.InputType"
import "android.graphics.Typeface"

local cjson = require "cjson"
local prefs = this.getSharedPreferences("RadioAppPrefs", 0)
local editor = prefs.edit()

local current_lang = Locale.getDefault().getLanguage()
if current_lang ~= "ar" and current_lang ~= "fr" and current_lang ~= "en" then
  current_lang = "en"
end

local strings = {
  ar = {
    title = "راديو الاذاعات العربيه",
    subtitle = "تم تطويره بواسطة سايح فتحي",
    player_title = "مشغل الراديو",
    hint_search = "بحث...",
    hint_limit = "الحد الأقصى",
    btn_telegram = "الانضمام لقناة التليجرام",
    btn_close = "إغلاق",
    btn_prev = "السابق",
    btn_next = "التالي",
    status_loading = "الحالة: جاري التحميل...",
    status_playing = "الحالة: جاري تشغيل ",
    status_paused = "الحالة: متوقف مؤقتاً",
    status_error = "الحالة: خطأ في الاتصال بهذه المحطة!",
    msg_loading_server = "جاري الاتصال بخادم الراديو...",
    msg_loading_stations = "جاري تحميل قائمة المحطات العربية...",
    msg_loading_country = "جاري تحميل بيانات الدولة...",
    msg_search_advanced = "جاري البحث المتقدم...",
    msg_error_connection = "خطأ في الاتصال!",
    msg_no_stations = "لا توجد محطات متاحة!",
    msg_no_match = "لم يتم العثور على محطات مطابقة!",
    msg_no_results = "لم يتم العثور على نتائج",
    msg_loaded_fav = "تم تحميل %d من المحطات المفضلة",
    msg_loaded_count = "تم تحميل %d من المحطات العربية",
    msg_loaded_total = "تم تحميل ما مجموعه %d محطة عربية",
    msg_found_results = "تم العثور على %d من النتائج",
    toast_fav_add = "تمت الإضافة إلى المفضلة",
    toast_fav_remove = "تمت الإزالة من المفضلة",
    speech_fav_add = "تم إضافة %s إلى المفضلة",
    speech_fav_remove = "تم إزالة %s من المفضلة",
    btn_pause = "إيقاف مؤقت",
    btn_play = "تشغيل",
    view_modes = {"جميع المحطات العربية", "محطات الراديو المفضلة"}
  },
  fr = {
    title = "Radio des stations arabes",
    subtitle = "Développé par Sayah Fethi",
    player_title = "Lecteur Radio",
    hint_search = "Recherche...",
    hint_limit = "Limite",
    btn_telegram = "Rejoindre Telegram",
    btn_close = "Fermer",
    btn_prev = "Précédent",
    btn_next = "Suivant",
    status_loading = "Statut: Chargement...",
    status_playing = "Statut: Lecture de ",
    status_paused = "Statut: En pause",
    status_error = "Statut: Échec de connexion à cette station!",
    msg_loading_server = "Connexion au serveur radio...",
    msg_loading_stations = "Chargement de la liste arabe...",
    msg_loading_country = "Chargement des données du pays...",
    msg_search_advanced = "Recherche avancée...",
    msg_error_connection = "Erreur de connexion!",
    msg_no_stations = "Aucune station disponible!",
    msg_no_match = "Aucune station correspondante trouvée!",
    msg_no_results = "Aucun résultat trouvé",
    msg_loaded_fav = "%d stations favorites chargées",
    msg_loaded_count = "%d stations arabes chargées",
    msg_loaded_total = "%d stations arabes chargées au total",
    msg_found_results = "%d résultats trouvés",
    toast_fav_add = "Ajouté aux favoris",
    toast_fav_remove = "Supprimé des favoris",
    speech_fav_add = "%s ajouté aux favoris",
    speech_fav_remove = "%s supprimé des favoris",
    btn_pause = "Pause",
    btn_play = "Lire",
    view_modes = {"Toutes les stations", "Stations favorites"}
  },
  en = {
    title = "Arabic Stations Radio",
    subtitle = "Developed by Sayah Fethi",
    player_title = "Radio Player",
    hint_search = "Search...",
    hint_limit = "Limit",
    btn_telegram = "Join Telegram Channel",
    btn_close = "Close",
    btn_prev = "Previous",
    btn_next = "Next",
    status_loading = "Status: Loading...",
    status_playing = "Status: Playing ",
    status_paused = "Status: Paused",
    status_error = "Status: Error connecting to this station!",
    msg_loading_server = "Connecting to radio server...",
    msg_loading_stations = "Loading Arabic station list...",
    msg_loading_country = "Loading country data...",
    msg_search_advanced = "Advanced searching...",
    msg_error_connection = "Connection error!",
    msg_no_stations = "No stations available!",
    msg_no_match = "No matching stations found!",
    msg_no_results = "No results found",
    msg_loaded_fav = "Loaded %d favorite stations",
    msg_loaded_count = "Loaded %d Arabic stations",
    msg_loaded_total = "Loaded a total of %d Arabic stations",
    msg_found_results = "Found %d results",
    toast_fav_add = "Added to favorites",
    toast_fav_remove = "Removed from favorites",
    speech_fav_add = "Added %s to favorites",
    speech_fav_remove = "Removed %s from favorites",
    btn_pause = "Pause",
    btn_play = "Play",
    view_modes = {"All Arabic Stations", "Favorite Stations"}
  }
}

local lang = strings[current_lang]

local api_servers = {
  "https://de1.api.radio-browser.info/json",
  "https://nl1.api.radio-browser.info/json",
  "https://at1.api.radio-browser.info/json",
  "https://all.api.radio-browser.info/json"
}

local active_api = ""
local station_list = {}
local favorites_list = {}
local current_station_index = 1
local player = nil
local player_status_tv = nil
local btn_play_pause = nil
local is_init_done = false

local sort_options = {
  {name = (current_lang == "ar" and "الأكثر نقراً" or (current_lang == "fr" and "Plus cliqués" or "Most clicked")), param = "order=clickcount&reverse=true"},
  {name = (current_lang == "ar" and "الأكثر تصويتاً" or (current_lang == "fr" and "Plus votés" or "Most voted")), param = "order=votes&reverse=true"},
  {name = (current_lang == "ar" and "الشائعة حالياً" or (current_lang == "fr" and "Tendances" or "Trending")), param = "order=clicktrend&reverse=true"},
  {name = (current_lang == "ar" and "الاسم من A إلى Z" or (current_lang == "fr" and "Nom A-Z" or "Name A-Z")), param = "order=name&reverse=false"},
  {name = (current_lang == "ar" and "الاسم من Z إلى A" or (current_lang == "fr" and "Nom Z-A" or "Name Z-A")), param = "order=name&reverse=true"},
  {name = (current_lang == "ar" and "عشوائي" or (current_lang == "fr" and "Aléatoire" or "Random")), param = "order=random&reverse=false"}
}

local country_options = {
  {name = (current_lang == "ar" and "كل الإذاعات العربية" or (current_lang == "fr" and "Toutes les radios arabes" or "All Arabic Radios")), code = "ALL"},
  {name = (current_lang == "ar" and "الجزائر" or (current_lang == "fr" and "Algérie" or "Algeria")), code = "DZ"},
  {name = (current_lang == "ar" and "البحرين" or (current_lang == "fr" and "Bahreïn" or "Bahrain")), code = "BH"},
  {name = (current_lang == "ar" and "جزر القمر" or (current_lang == "fr" and "Comores" or "Comoros")), code = "KM"},
  {name = (current_lang == "ar" and "جيبوتي" or (current_lang == "fr" and "Djibouti" or "Djibouti")), code = "DJ"},
  {name = (current_lang == "ar" and "مصر" or (current_lang == "fr" and "Égypte" or "Egypt")), code = "EG"},
  {name = (current_lang == "ar" and "العراق" or (current_lang == "fr" and "Irak" or "Iraq")), code = "IQ"},
  {name = (current_lang == "ar" and "الأردن" or (current_lang == "fr" and "Jordanie" or "Jordan")), code = "JO"},
  {name = (current_lang == "ar" and "الكويت" or (current_lang == "fr" and "Koweït" or "Kuwait")), code = "KW"},
  {name = (current_lang == "ar" and "لبنان" or (current_lang == "fr" and "Liban" or "Lebanon")), code = "LB"},
  {name = (current_lang == "ar" and "ليبيا" or (current_lang == "fr" and "Libye" or "Libya")), code = "LY"},
  {name = (current_lang == "ar" and "موريتانيا" or (current_lang == "fr" and "Mauritanie" or "Mauritania")), code = "MR"},
  {name = (current_lang == "ar" and "المغرب" or (current_lang == "fr" and "Maroc" or "Morocco")), code = "MA"},
  {name = (current_lang == "ar" and "عمان" or (current_lang == "fr" and "Oman" or "Oman")), code = "OM"},
  {name = (current_lang == "ar" and "فلسطين" or (current_lang == "fr" and "Palestine" or "Palestine")), code = "PS"},
  {name = (current_lang == "ar" and "قطر" or (current_lang == "fr" and "Qatar" or "Qatar")), code = "QA"},
  {name = (current_lang == "ar" and "المملكة العربية السعودية" or (current_lang == "fr" and "Arabie Saoudite" or "Saudi Arabia")), code = "SA"},
  {name = (current_lang == "ar" and "الصومال" or (current_lang == "fr" and "Somalie" or "Somalia")), code = "SO"},
  {name = (current_lang == "ar" and "السودان" or (current_lang == "fr" and "Soudan" or "Sudan")), code = "SD"},
  {name = (current_lang == "ar" and "سوريا" or (current_lang == "fr" and "Syrie" or "Syria")), code = "SY"},
  {name = (current_lang == "ar" and "تونس" or (current_lang == "fr" and "Tunisie" or "Tunisia")), code = "TN"},
  {name = (current_lang == "ar" and "الإمارات العربية المتحدة" or (current_lang == "fr" and "Émirats Arabes Unis" or "UAE")), code = "AE"},
  {name = (current_lang == "ar" and "اليمن" or (current_lang == "fr" and "Yémen" or "Yemen")), code = "YE"}
}

local function speakText(text)
  if service and service.speak then
    service.speak(text)
  end
end

local function loadFavorites()
  local fav_json = prefs.getString("favorites", "[]")
  favorites_list = cjson.decode(fav_json)
end

local function saveFavorites()
  editor.putString("favorites", cjson.encode(favorites_list))
  editor.apply()
end

local function isFavorite(url)
  for i, v in ipairs(favorites_list) do
    if v.url == url then return i end
  end
  return false
end

local function releasePlayer()
  if player ~= nil then
    pcall(function()
      if player.isPlaying() then player.stop() end
      player.release()
    end)
    player = nil
  end
end

local function toggleLoadingState(isLoading, message)
  if isLoading then
    tv_loading.setText(message or lang.status_loading)
    tv_loading.setVisibility(View.VISIBLE)
    lv_stations.setVisibility(View.GONE)
  else
    tv_loading.setVisibility(View.GONE)
    lv_stations.setVisibility(View.VISIBLE)
  end
end

local function renderList(data, adapter, success_message)
  station_list = data
  adapter.clear()
  for i=1, #data do
    adapter.add(data[i].name)
  end
  adapter.notifyDataSetChanged()
  if success_message then
    speakText(success_message)
  end
end

local function getLimitValue()
  local val = edt_limit.getText().toString()
  if val == nil or val == "" then return "1000" end
  local num = tonumber(val)
  if not num or num <= 0 then return "1000" end
  return tostring(math.floor(num))
end

local function reloadData(adapter, query)
  if not is_init_done then return end
  query = query or ""
  local view_mode = spin_view.getSelectedItemPosition()
  local sort_idx = spin_sort.getSelectedItemPosition() + 1
  local sort_param = sort_options[sort_idx].param
  local cty_idx = spin_country.getSelectedItemPosition() + 1
  local cty_code = country_options[cty_idx].code
  local limit_val = getLimitValue()

  if view_mode == 1 then
    local filtered = {}
    for i, v in ipairs(favorites_list) do
      if query == "" or string.find(string.lower(v.name), string.lower(query), 1, true) then
        table.insert(filtered, {name = v.name, url = v.url, stationuuid = v.stationuuid})
      end
    end
    toggleLoadingState(false)
    renderList(filtered, adapter, string.format(lang.msg_loaded_fav, #filtered))
    return
  end

  if query ~= "" then
    toggleLoadingState(true, lang.msg_search_advanced)
    local tokens = {}
    for token in string.gmatch(query, "%S+") do
      table.insert(tokens, token)
    end
    if #tokens == 0 then table.insert(tokens, "") end
    
    local discovery_limit = math.max(tonumber(limit_val) or 1000, 10000)
    local base_params = "limit=" .. discovery_limit .. "&order=votes&reverse=true&hidebroken=true&language=arabic"
    if cty_code ~= "ALL" then
      base_params = base_params .. "&countrycode=" .. cty_code
    end
    
    local urls = {}
    for i, token in ipairs(tokens) do
      local encoded = URLEncoder.encode(token, "UTF-8")
      table.insert(urls, active_api .. "/stations/search?" .. base_params .. "&name=" .. encoded)
      table.insert(urls, active_api .. "/stations/search?" .. base_params .. "&tag=" .. encoded)
    end
    
    local expected_responses = #urls
    local completed_responses = 0
    local seen_stations = {}
    local any_success = false
    
    for i, url in ipairs(urls) do
      Http.get(url, function(code, content)
        completed_responses = completed_responses + 1
        if code == 200 then
          any_success = true
          local success, data = pcall(cjson.decode, content)
          if success and type(data) == "table" then
            for j=1, #data do
              local s = data[j]
              local uid = s.stationuuid
              if uid and uid ~= "" and not seen_stations[uid] then
                seen_stations[uid] = s
              end
            end
          end
        end
        
        if completed_responses == expected_responses then
          if not any_success then
            toggleLoadingState(true, lang.msg_error_connection)
            return
          end
          local filtered = {}
          for uid, s in pairs(seen_stations) do
            local is_match = true
            local haystack = string.lower((s.name or "") .. " " .. (s.tags or ""))
            for _, token in ipairs(tokens) do
              if not string.find(haystack, string.lower(token), 1, true) then
                is_match = false
                break
              end
            end
            if is_match then
              table.insert(filtered, s)
            end
          end
          table.sort(filtered, function(a, b)
            local vA = tonumber(a.votes) or 0
            local vB = tonumber(b.votes) or 0
            return vA > vB
          end)
          
          local final_limit = tonumber(limit_val) or 1000
          local result = {}
          for j=1, math.min(#filtered, final_limit) do
            table.insert(result, {name = filtered[j].name, url = filtered[j].url_resolved, stationuuid = filtered[j].stationuuid})
          end
          toggleLoadingState(false)
          if #result == 0 then
            toggleLoadingState(true, lang.msg_no_match)
            speakText(lang.msg_no_results)
          else
            renderList(result, adapter, string.format(lang.msg_found_results, #result))
          end
        end
      end)
    end
  elseif cty_code ~= "ALL" then
    toggleLoadingState(true, lang.msg_loading_country)
    local params = "limit=" .. limit_val .. "&" .. sort_param .. "&countrycode=" .. cty_code .. "&language=arabic&hidebroken=true"
    local search_url = active_api .. "/stations/search?" .. params
    Http.get(search_url, function(code, content)
      if code == 200 then
        local success, data = pcall(cjson.decode, content)
        local result = {}
        if success and type(data) == "table" then
          for i=1, #data do
            table.insert(result, {name = data[i].name, url = data[i].url_resolved, stationuuid = data[i].stationuuid})
          end
        end
        toggleLoadingState(false)
        if #result == 0 then
          toggleLoadingState(true, lang.msg_no_stations)
        else
          renderList(result, adapter, string.format(lang.msg_loaded_count, #result))
        end
      else
        toggleLoadingState(true, lang.msg_error_connection)
      end
    end)
  else
    toggleLoadingState(true, lang.msg_loading_stations)
    local global_arabic_url = active_api .. "/stations/search?language=arabic&limit=" .. limit_val .. "&" .. sort_param .. "&hidebroken=true"
    
    Http.get(global_arabic_url, function(code, content)
      if code == 200 then
        local success, data = pcall(cjson.decode, content)
        local result = {}
        if success and type(data) == "table" then
          for i=1, #data do
            table.insert(result, {name = data[i].name, url = data[i].url_resolved, stationuuid = data[i].stationuuid})
          end
        end
        toggleLoadingState(false)
        if #result == 0 then
          toggleLoadingState(true, lang.msg_no_stations)
        else
          renderList(result, adapter, string.format(lang.msg_loaded_total, #result))
        end
      else
        toggleLoadingState(true, lang.msg_error_connection)
      end
    end)
  end
end

local function findFastestServerAndStart(adapter)
  local check_count = 0
  local found = false
  toggleLoadingState(true, lang.msg_loading_server)
  for i=1, #api_servers do
    Http.get(api_servers[i] .. "/stats", function(code, content)
      if found then return end
      if code == 200 then
        found = true
        active_api = api_servers[i]
        is_init_done = true
        reloadData(adapter, "")
      end
      check_count = check_count + 1
      if check_count == #api_servers and not found then
        active_api = "https://all.api.radio-browser.info/json"
        is_init_done = true
        reloadData(adapter, "")
      end
    end)
  end
end

local function updatePlayerStatus(text)
  if player_status_tv then player_status_tv.setText(text) end
end

local function playStation(index)
  if index < 1 or index > #station_list then return end
  current_station_index = index
  local station = station_list[index]
  releasePlayer()
  updatePlayerStatus(lang.status_loading:gsub("%.%.%.", " ") .. station.name .. "...")
  if btn_play_pause then btn_play_pause.setText(lang.btn_pause) end
  
  player = MediaPlayer()
  player.setDataSource(station.url)
  player.setOnPreparedListener(MediaPlayer.OnPreparedListener{
    onPrepared = function(mp)
      mp.start()
      updatePlayerStatus(lang.status_playing .. station.name)
    end
  })
  player.setOnErrorListener(MediaPlayer.OnErrorListener{
    onError = function(mp, what, extra)
      updatePlayerStatus(lang.status_error)
      return true
    end
  })
  player.prepareAsync()
end

local function showPlayerDialog()
  local playerLayout = {
    LinearLayout,
    orientation = "vertical",
    layout_width = "fill",
    layout_height = "wrap_content",
    padding = "16dp",
    {TextView,
      id = "tv_status",
      text = lang.status_loading,
      textSize = "16sp",
      layout_width = "fill",
      layout_height = "wrap_content",
      layout_marginBottom = "20dp",
    },
    {LinearLayout,
      orientation = "horizontal",
      layout_width = "fill",
      layout_height = "wrap_content",
      gravity = "center",
      {Button, text = lang.btn_prev, onClick = function() if current_station_index > 1 then playStation(current_station_index - 1) end end},
      {Button, id = "btn_toggle", text = lang.btn_pause, onClick = function()
        if player then
          if player.isPlaying() then
            player.pause()
            btn_toggle.setText(lang.btn_play)
            updatePlayerStatus(lang.status_paused)
          else
            player.start()
            btn_toggle.setText(lang.btn_pause)
            updatePlayerStatus(lang.status_playing)
          end
        end
      end},
      {Button, text = lang.btn_next, onClick = function() if current_station_index < #station_list then playStation(current_station_index + 1) end end}
    },
    {Button, text = lang.btn_close, layout_width = "fill", layout_height = "wrap_content", layout_marginTop = "10dp", onClick = function() playerDlg.dismiss() end}
  }
  playerDlg = LuaDialog(this)
  playerDlg.setView(loadlayout(playerLayout))
  playerDlg.setTitle(lang.player_title)
  player_status_tv = tv_status
  btn_play_pause = btn_toggle
  playerDlg.setOnDismissListener(DialogInterface.OnDismissListener{
    onDismiss = function(dialog) releasePlayer() end
  })
  playerDlg.show()
  playStation(current_station_index)
end

function showThinhDialog()
  loadFavorites()
  local adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1)
  local view_mode_items = lang.view_modes
  local sort_mode_items = {}
  for i=1, #sort_options do table.insert(sort_mode_items, sort_options[i].name) end
  local cty_mode_items = {}
  for i=1, #country_options do table.insert(cty_mode_items, country_options[i].name) end
  
  local adapter_view_mode = ArrayAdapter(this, android.R.layout.simple_spinner_item, view_mode_items)
  adapter_view_mode.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
  local adapter_sort = ArrayAdapter(this, android.R.layout.simple_spinner_item, sort_mode_items)
  adapter_sort.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
  local adapter_country = ArrayAdapter(this, android.R.layout.simple_spinner_item, cty_mode_items)
  adapter_country.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
  
  local thinhLayout = {
    LinearLayout,
    orientation = LinearLayout.VERTICAL,
    layout_width = "fill",
    layout_height = "fill",
    {LinearLayout,
      orientation = "vertical",
      layout_width = "fill",
      layout_height = "wrap_content",
      padding = "8dp",
      gravity = "center",
      {TextView,
        id = "tv_main_title",
        text = lang.title,
        textSize = "20sp",
        textColor = 0xFF333333,
        layout_width = "wrap_content",
        layout_height = "wrap_content"
      },
      {TextView,
        text = lang.subtitle,
        textSize = "14sp",
        textColor = 0xFF666666,
        layout_width = "wrap_content",
        layout_height = "wrap_content",
        layout_marginTop = "2dp"
      }
    },
    {LinearLayout,
      orientation = "horizontal",
      layout_width = "fill",
      layout_height = "wrap_content",
      {Spinner, id = "spin_view", layout_weight = 1, adapter = adapter_view_mode},
      {Spinner, id = "spin_sort", layout_weight = 1, adapter = adapter_sort},
      {Spinner, id = "spin_country", layout_weight = 1, adapter = adapter_country}
    },
    {LinearLayout,
      orientation = "horizontal",
      layout_width = "fill",
      layout_height = "wrap_content",
      layout_marginTop = "5dp",
      {EditText,
        id = "edt_limit",
        hint = lang.hint_limit,
        text = "1000",
        inputType = InputType.TYPE_CLASS_NUMBER,
        layout_width = "wrap_content",
        layout_height = "wrap_content",
        width = "80dp",
        gravity = "center",
      },
      {EditText,
        id = "edt_search",
        hint = lang.hint_search,
        layout_width = "0dp",
        layout_height = "wrap_content",
        layout_weight = 1,
      }
    },
    {TextView,
      id = "tv_loading",
      text = lang.status_loading,
      textSize = "15sp",
      gravity = "center",
      layout_width = "fill",
      layout_height = "0dp",
      layout_weight = 1,
      visibility = "gone",
    },
    {ListView,
      id = "lv_stations",
      layout_width = "fill",
      layout_height = "0dp",
      layout_weight = 1,
      adapter = adapter,
    },
    {LinearLayout,
      orientation = "horizontal",
      layout_width = "fill",
      layout_height = "wrap_content",
      {Button,
        text = lang.btn_telegram,
        layout_width = "0dp",
        layout_height = "wrap_content",
        layout_weight = 1,
        onClick = function()
          local intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://t.me/+6zjYEyrtoL00NDQ0"))
          service.startActivity(intent)
          thinhdlg.dismiss()
        end},
      {Button,
        text = lang.btn_close,
        layout_width = "0dp",
        layout_height = "wrap_content",
        layout_weight = 1,
        onClick = function()
          thinhdlg.dismiss()
        end}}}
        
  thinhdlg = LuaDialog(this)
  thinhdlg.setView(loadlayout(thinhLayout))
  
  if tv_main_title then
    tv_main_title.setTypeface(Typeface.DEFAULT_BOLD)
  end
  
  spin_view.setSelection(prefs.getInt("view_mode", 0))
  spin_sort.setSelection(prefs.getInt("sort_mode", 3))
  spin_country.setSelection(prefs.getInt("country_mode", 0))
  
  spin_view.setOnItemSelectedListener(AdapterView.OnItemSelectedListener{
    onItemSelected = function(parent, view, position, id)
      editor.putInt("view_mode", position).apply()
      reloadData(adapter, edt_search.getText().toString())
    end
  })
  spin_sort.setOnItemSelectedListener(AdapterView.OnItemSelectedListener{
    onItemSelected = function(parent, view, position, id)
      editor.putInt("sort_mode", position).apply()
      reloadData(adapter, edt_search.getText().toString())
    end
  })
  spin_country.setOnItemSelectedListener(AdapterView.OnItemSelectedListener{
    onItemSelected = function(parent, view, position, id)
      editor.putInt("country_mode", position).apply()
      reloadData(adapter, edt_search.getText().toString())
    end
  })
  lv_stations.setOnItemClickListener(AdapterView.OnItemClickListener{
    onItemClick = function(parent, view, position, id)
      current_station_index = position + 1
      showPlayerDialog()
    end
  })
  lv_stations.setOnItemLongClickListener(AdapterView.OnItemLongClickListener{
    onItemLongClick = function(parent, view, position, id)
      local item_idx = position + 1
      local station = station_list[item_idx]
      if not station then return false end
      
      local f_idx = isFavorite(station.url)
      if f_idx then
        table.remove(favorites_list, f_idx)
        Toast.makeText(this, lang.toast_fav_remove, Toast.LENGTH_SHORT).show()
        speakText(string.format(lang.speech_fav_remove, station.name))
      else
        table.insert(favorites_list, {name = station.name, url = station.url, stationuuid = station.stationuuid})
        Toast.makeText(this, lang.toast_fav_add, Toast.LENGTH_SHORT).show()
        speakText(string.format(lang.speech_fav_add, station.name))
      end
      saveFavorites()
      
      if spin_view.getSelectedItemPosition() == 1 then
        reloadData(adapter, edt_search.getText().toString())
      end
      return true
    end
  })
  thinhdlg.setOnDismissListener(DialogInterface.OnDismissListener{
    onDismiss = function(dialog) releasePlayer() end
  })
  thinhdlg.show()
  
  local searchHandler = Handler()
  local searchRunnable = nil
  local function onInputChanged()
    if searchRunnable ~= nil then searchHandler.removeCallbacks(searchRunnable) end
    local query = edt_search.getText().toString()
    searchRunnable = Runnable{
      run = function()
        reloadData(adapter, query)
      end
    }
    searchHandler.postDelayed(searchRunnable, 1000)
  end
  
  edt_search.addTextChangedListener(TextWatcher{onTextChanged = onInputChanged})
  edt_limit.addTextChangedListener(TextWatcher{onTextChanged = onInputChanged})
  findFastestServerAndStart(adapter)
end

showThinhDialog()